Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: change Interest-combining to play nicer with multiple subscribers #920

Merged
merged 7 commits into from
Aug 13, 2020

Conversation

hawkw
Copy link
Member

@hawkw hawkw commented Aug 12, 2020

Motivation

Currently, when multiple subscribers are in use, the way that Interest
values are combined results in surprising behavior.

If any subscriber returns Interest::always for a callsite, that
interest currently takes priority over any other Interests. This means
that if two threads have separate subscribers, one of which enables a
callsite always, and the other never, both subscribers will always
record that callsite, without the option to disable it. This is not
quite right. Instead, we should check whether the current subscriber
will enable that callsite in this case.

Solution

This branch changes the rules for combining Interests so that always
is only returned if both Interests are always. If only one is
always, the combined interest is now sometimes, instead. This means
that when multiple subscribers exist, enabled will always be called on
the current subscriber, except when all subscribers have indicated
that they are always or never interested in a callsite.

I've added tests that reproduce the issues with leaky filters.

Fixing this revealed an additional issue where tracing-subscriber's
EnvFilter assumes that enabled is only called for events, and never
for spans, because it always returns either always or never from
register_callsite for spans. Therefore, the dynamic span matching
directives that might enable a span were never checked in enabled.
However, under the new interest-combining rules, enabled may still
be called even if a subscriber returns an always or never interest,
if another subscriber exists that returned an incompatible interest.
This PR fixes that, as well.

Depends on #919

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
@hawkw hawkw requested review from yaahc and a team August 12, 2020 21:40
@hawkw
Copy link
Member Author

hawkw commented Aug 12, 2020

cc @jeromegn, this should fix the filter leaking you reported in #902.

However, we still need to figure out a better strategy for invalidating the callsite cache at the end of a subscriber lifetime, for performance reasons. This change fixes incorrect filtering by ensuring that the current subscriber is always asked whether it will enable a span/event when multiple subscribers disagree on whether or not it should be enabled. However, this means that if we have two subscribers, and one is dropped, we will continue asking the current subscriber for every callsite, even if it would rather return a cached interest, so that we can skip filtering.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Copy link
Collaborator

@yaahc yaahc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, sounds good

Comment on lines +31 to +45
// Iterate over the subscribers in the registry, and — if they are
// active — register the callsite with them.
let mut interests = self
.dispatchers
.iter()
.filter_map(|registrar| registrar.try_register(meta));

// Use the first subscriber's `Interest` as the base value.
let interest = if let Some(interest) = interests.next() {
// Combine all remaining `Interest`s.
interests.fold(interest, Interest::and)
} else {
// If nobody was interested in this thing, just return `never`.
Interest::never()
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@hawkw hawkw merged commit 906608a into eliza/nice-debug-output Aug 13, 2020
hawkw added a commit that referenced this pull request Aug 13, 2020
…bers (#927)

## Motivation

Currently, when multiple subscribers are in use, the way that `Interest`
values are combined results in surprising behavior. 

If _any_ subscriber returns `Interest::always` for a callsite, that
interest currently takes priority over any other `Interest`s. This means
that if two threads have separate subscribers, one of which enables a
callsite `always`, and the other `never`, _both_ subscribers will always
record that callsite, without the option to disable it. This is not
quite right. Instead, we should check whether the current subscriber
will enable that callsite in this case.

This issue is described in #902. 

## Solution

This branch changes the rules for combining `Interest`s so that `always`
is only returned if _both_ `Interest`s are `always`. If only _one_ is
`always`, the combined interest is now `sometimes`, instead. This means
that when multiple subscribers exist, `enabled` will always be called on
the _current_ subscriber, except when _all_ subscribers have indicated
that they are `always` or `never` interested in a callsite.

I've added tests that reproduce the issues with leaky filters.

Fixing this revealed an additional issue where `tracing-subscriber`'s
`EnvFilter` assumes that `enabled` is only called for events, and never
for spans, because it always returns either `always` or `never` from
`register_callsite` for spans. Therefore, the dynamic span matching
directives that might enable a span were never checked in `enabled`.
However, under the new interest-combining rules, `enabled` *may* still
be called even if a subscriber returns an `always` or `never` interest,
if *another* subscriber exists that returned an incompatible interest.
This PR fixes that, as well.

Depends on #919 

This was previously approved by @yaahc as PR #920, but I 
accidentally merged it into #919 _after_ that branch merged, rather
than into master (my bad!).

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants